﻿using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents.Serialization;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Xml.Serialization;
using Accord.Math;
using Accord.Math.Convergence;
using Accord.Math.Optimization;
using CoreClasses;
using LayoutPlanning.CollaborationService;
using Mvvm;
using LayoutPlanning.Helpers;
using LayoutPlanning.Model;
using LayoutPlanning.Model.ImmaModels;
using LayoutPlanning.Views;
using Microsoft.Win32;
using Mvvm.Native;
using PropertyTools.Wpf;
using Brushes = System.Windows.Media.Brushes;
using Distance = MathNet.Numerics.Distance;
using Point = System.Windows.Point;
using Rectangle = System.Drawing.Rectangle;
using RelayCommand = LayoutPlanning.Helpers.RelayCommand;
using Station = LayoutPlanning.Model.Station;

namespace LayoutPlanning.ViewModel
{
    public class StationRelationsViewModel : ViewModelBase
    {
        private static bool _running;
        private readonly Model.Point _offset = new Model.Point();
        private readonly ISaveLoadStationRelationService _service;
        private bool _box;
        private bool _dragOffset;
        private DrawType _drawNewSquare ;
        private string _importFile;
        private GeneralTask _newTask;
        private LayoutResults _results;
        private GraficalObject _selectedGraficalObject;
        private ListBoxItem _selectedNewTask;
        private bool _showNegativeInfluence;
        private bool _showPositiveInfluence;
        private bool _showSound;
        private bool _showSpagetti;

        private Point _startpoint;
        private double _zoom = 0.001;
        private double _soundSensitivness = 1000;
        private bool  _optimaizationRunning =true;
        private int _iterations;
        private NelderMead _optimization;


        public StationRelationsViewModel(ISaveLoadStationRelationService modelService)
        {
            _service = modelService;

            Initialize().Wait();
            Model.DeployedGraficalObjects.CollectionChanged += DeployedGraficalObjects_CollectionChanged;
            Offset.PropertyChanged += Offset_PropertyChanged;
            // var timer = new DispatcherTimer(TimeSpan.FromSeconds(1),DispatcherPriority.Background, Refresh, Dispatcher.CurrentDispatcher );
            //timer.Start();
        }

        public StationRelationModel Model { get; private set; }

        public ObservableCollection<Station> Stations => Model.LoadedStations;

        //Model.StationRelations.Select(t => t.Station1)
        //    .Union(Model.StationRelations.Select(y => y.Station2))
        //    .Distinct()
        //    .OrderBy(k => k.Name);

        public RelayCommand<UIElement> PrintView => new RelayCommand<UIElement>(t =>
        {
            PrintDialog printDlg = new PrintDialog();
            printDlg.ShowDialog();
            printDlg.PrintVisual(t, "Printing.");
        });
        public RelayCommand AddStation
        {
            get
            {
                return new RelayCommand(async () =>
                {
                    var t = new Task(() =>
                    {
                        var newStation = new Station() {Name = ""};
                        AddStationToLayout(newStation);
                    });
                    t.Start();
                    await t;
                    await ViewModelLocator.Dispatcher.InvokeAsync(() => RaisePropertyChanged(nameof(Stations)));
                });
            }
        }

        public RelayCommand<Station> LoadStation
        {
            get
            {
                return new RelayCommand<Station>((t) =>
                {
                    Model.LoadedStations?.Add(t);
                    ViewModelLocator.Stations.Stations.Remove(t);
                });
            }
        }

        public RelayCommand OptimizePositionsOfMachines
        {
            get
            {
                return new RelayCommand( async () =>
                {

                    if(!DeployedStations.Any())
                        return;
                    if (Optimization != null && !OptimaizationNotRunning)
                    {
                        Optimization.Convergence.Cancel = true;
                        

                        return;
                    }

                    _stations = DeployedStations.ToArray();

                    var opt= new Accord.Math.Optimization.NelderMead(_stations.Length * 3, PointsForSolution);//, func, new[] {contrain});
                   
                    List<double> posstart = new List<double>();
                    int k = 0;
                    foreach (var station in _stations)
                    {
                        var p = ViewModelLocator.Layouts.SelectedProject;
                        posstart.Add(station.Position.X);
                        posstart.Add(station.Position.Y);
                        posstart.Add(station.Angle);
                        opt.UpperBounds[k*3] = p.PosX +p.SizeX - station.SizeX;
                       opt.UpperBounds[k*3+1] = p.PosY + p.SizeY - station.SizeY;
                        opt.UpperBounds[k*3 + 2] = 360;
                        opt.LowerBounds[k*3] = p.PosX;
                       opt.LowerBounds[k * 3+1] = p.PosY;
                        opt.LowerBounds[k * 3 + 2] = 0;
                        opt.StepSize[k*3 ] = p.SizeX/2;
                        opt.StepSize[k * 3 + 1] = p.SizeY/2;
                        opt.StepSize[k * 3 + 2] = 10;
                        opt.Convergence.AbsoluteParameterTolerance[k * 3] = 1;
                        opt.Convergence.AbsoluteParameterTolerance[k * 3 + 1] = 1;
                        opt.Convergence.AbsoluteParameterTolerance[k * 3 + 2] = 1;
                        k++;
                    }

                    area = ViewModelLocator.Layouts.SelectedProject.Project.ProjectAreaDef; 
                    

                    OptimaizationNotRunning = false;
                    Optimization = opt;
                    opt.Convergence.StartTime = DateTime.Now;
                    var ct = new CancellationTokenSource();
                    Task optimizationTask = Task.Run(() =>
                    {
                        
                                                     opt.Minimize(posstart.ToArray());
                        Optimization.Convergence.Cancel = true;
                    },ct.Token);

                    Task statusTask = Task.Run(() =>
                    {
                        DispatcherOperation ta=null;

                        while (true)
                        {
                            Task.Delay(1000).Wait();

                            if (Optimization.Convergence.Cancel)
                            {
                                ct.Cancel();
                                return;
                            }

                            if(ta == null || ta.Status != DispatcherOperationStatus.Pending)
                                ta = _stations[0].Control.Dispatcher.InvokeAsync(() =>
                                {
                                    RaisePropertyChanged(nameof(OptimizationButtonText));
                                   

                                }, DispatcherPriority.Normal);
                        }
                    });

                    await optimizationTask;

                    OptimaizationNotRunning = true;

                    int i = 0;
                    foreach (var station in DeployedStations)
                    {
                        station.Position.X = opt.Solution[i];
                        i++;
                        station.Position.Y = opt.Solution[i];
                        i++;
                        station.Angle = opt.Solution[i];
                        i++;

                    }

                });
            }
        }


        public string OptimizationButtonText
            =>
                OptimaizationNotRunning
                    ? "Optimize Machine Positions"
                    : "Optimization Running Click to Cancel, Iterations: " + OptimizationIterations + " Seconds: " + (int) OptimizationTime.TotalSeconds;

        public int OptimizationIterations => Optimization?.Convergence?.Evaluations??0;
        public TimeSpan OptimizationTime => DateTime.Now -(Optimization==null? DateTime.Now : Optimization.Convergence.StartTime);
        public NelderMead Optimization
        {
            get { return _optimization; }
            private set
            {
                _optimization = value;
                RaisePropertyChanged();
            }
        }

        public bool OptimaizationNotRunning
        {
            get { return _optimaizationRunning; }
            set
            {
                _optimaizationRunning = value;
                RaisePropertyChanged();
                RaisePropertyChanged(nameof(OptimizationButtonText));
            }
        }

        private static Station[] _stations;
        private static Rectangle area;

        private double PointsForSolution(double[] doubles)
        {

            if (_stations.Length == 0 || _stations[0] == null)
                return Double.MaxValue;

            double returnvalue = 0d;
            _stations[0].Control.Dispatcher.Invoke(() =>
            {
                for (int i = 0; i < _stations.Length; i++)
                {
                    _stations[i].Position.X = doubles[i * 3];
                    _stations[i].Position.Y = doubles[i * 3 + 1];
                    _stations[i].Angle = doubles[i * 3 + 2];
                }
            }, DispatcherPriority.Normal);
            foreach (var t in _stations)
            {

                if(CrossingBorder(t.Control, area ))
                    return Double.MaxValue;
                var list = OverlappingStations(t);
                if (list.Result.Any())
                {
                    return  double.MaxValue;
                    
                }
                
            }
          
            if(returnvalue!=0d)
                return returnvalue;

            var relations = CreateRelations();
            returnvalue = relations.Sum(t => t.Value);
            if (returnvalue == Double.NaN)
            {
                returnvalue = double.MaxValue;
                return returnvalue;
            }
               
            

            return returnvalue;

        }

        private List<StationRelation> CreateRelations()
        {
            var relations = new List<StationRelation>();
            for (int i = 0; i < _stations.Length; i++)
            {
                for (int ii = i + 1; ii < _stations.Length; ii++)
                {
                    int transports =
                        CurrentWorkOrder.Worktasks.Count(
                            (t) =>
                                t.TaskToStation == _stations[i] && t.TaskFromStation == _stations[ii] ||
                                t.TaskToStation == _stations[ii] && t.TaskFromStation == _stations[i]);

                    double distance =
                        Math.Sqrt(Math.Pow(Math.Abs(_stations[i].Position.X - _stations[ii].Position.X), 2) +
                                  Math.Pow(Math.Abs(_stations[i].Position.Y - _stations[ii].Position.Y), 2));

                    double soundPenelty = _stations[i].SoundSensitiv || _stations[ii].SoundSensitiv
                        ? (Math.Pow(SoundSensitivness , (double)_stations[i].Loudness)+ Math.Pow(SoundSensitivness , (double)_stations[ii].Loudness)) /distance
                        : 0;
                    double specialRelations = 0;
                    if (_stations[i].SpecialRelations != null)
                    {
                        var specialRelations1 =
                            _stations[i].SpecialRelations.Where(t => t.RelationTo == _stations[ii])
                                .Sum(p => SoundSensitivness* ((double)p.TypeOfRelation)/10d);
                        specialRelations += specialRelations1 < 0 ? (specialRelations1*-1)/distance:specialRelations1*distance;
                    }
                    if (_stations[ii].SpecialRelations != null)
                    {
                        var specialRelations2 =
                            _stations[ii].SpecialRelations.Where(t => t.RelationTo == _stations[i])
                                .Sum(p => SoundSensitivness *((double)  p.TypeOfRelation)/10d);
                        specialRelations += specialRelations2 < 0 ? (specialRelations2 * -1) / distance : specialRelations2 * distance;
                    }


                    relations.Add(new StationRelation(_stations[i], _stations[ii], (transports == 0?0: distance/transports) + soundPenelty + specialRelations));
                }
            }
            return relations;
        }

        public double SoundSensitivness
        {
            get { return _soundSensitivness; }
            set
            {
                _soundSensitivness = value;
                RaisePropertyChanged();
            }
        }


        public Workorder CurrentWorkOrder
        {
            get { return Model.CurrentWorkOrder; }
            set
            {
                Model.CurrentWorkOrder = value;
                RaisePropertyChanged();
            }
        }

        public bool ShowSound
        {
            get { return _showSound; }
            set
            {
                _showSound = value;
                RaisePropertyChanged();
            }
        }

        public bool ShowSpagetti
        {
            get { return _showSpagetti; }
            set
            {
                _showSpagetti = value;
                RaisePropertyChanged();
            }
        }

        public bool ShowNegativeInfluence
        {
            get { return _showNegativeInfluence; }
            set
            {
                _showNegativeInfluence = value;
                RaisePropertyChanged();
            }
        }

        public bool ShowPositiveInfluence
        {
            get { return _showPositiveInfluence; }
            set
            {
                _showPositiveInfluence = value;
                RaisePropertyChanged();
            }
        }

        public string ImportFile
        {
            get { return _importFile; }
            set
            {
                _importFile = value;
                RaisePropertyChanged();
            }
        }

        public bool Box
        {
            get { return _box; }
            set
            {
                _box = value;
                RaisePropertyChanged();
            }
        }

        public ObservableCollection<GraficalObject> DeployedGraficalObjects => Model.DeployedGraficalObjects;

        public IEnumerable<Station> DeployedStations => Model.DeployedGraficalObjects.OfType<Station>();

        public IEnumerable<GraficalObject> DeployedStoragesMachines
            =>
                Model.DeployedGraficalObjects.Where(
                    t => t.GetType() == typeof(Station) || t.GetType() == typeof(GraficalStorageSpace));

        public IEnumerable<GraficalStorageSpace> DeployedStorages => Model.DeployedGraficalObjects.OfType<GraficalStorageSpace>();

        public IEnumerable<GraficalObstacle> Obstacles => Model.DeployedGraficalObjects.OfType<GraficalObstacle>();

        public RelayCommand<GraficalObject> NewDrop
        {
            get
            {
                return new RelayCommand<GraficalObject>((graficalObject =>
                {
                    var stationc = graficalObject as Station;
                    if (stationc != null)
                        AddStationToLayout(stationc);

                    //graficalObject.RefreshPos();
                }));
            }
        }

        public LayoutModel Layout
        {
            get { return Model.Layout; }
            set
            {
                Model.Layout = value;
                RaisePropertyChanged();
                
            }
        }

        public RelayCommand<ZoomableCanvas> NormalizeZoom => new RelayCommand<ZoomableCanvas>(t =>
        {
            var res = Math.Min(t.ActualHeight/Layout.Height, t.ActualWidth/Layout.Width);
            Zoom = res;
            Offset.X = 0;
            Offset.Y = 0;
        });

        public RelayCommand LoadStationCommand
        {
            get
            {
                return new RelayCommand(() =>
                {
                    ViewModelLocator.Stations.Reset();
                    var l = new LoadStationView();
                    l.ShowDialog();
                });
            }
        }

        public GraficalObject SelectedGraficalObject
        {
            get { return _selectedGraficalObject; }
            set
            {
                if (_selectedGraficalObject == value) return;
                _selectedGraficalObject = value;
                RaisePropertyChanged();

                if (value is Station)
                {
                    (value as Station).SelectedWorkGripPosition = null;
                    RaisePropertyChanged(nameof(SelectedStation));
                }
                RaisePropertyChanged(nameof(SelectedStorage));
            }
        }

        public Station SelectedStation => _selectedGraficalObject as Station;
        public GraficalStorageSpace SelectedStorage => SelectedGraficalObject as GraficalStorageSpace;

        public RelayCommand Reset
        {
            get
            {
                return new RelayCommand(() =>
                {
                    Model.DeployedGraficalObjects.Clear();

                    Model.LoadedStations.Clear();
                    ViewModelLocator.Stations.Reset();
                    SelectedGraficalObject = null;
                    Layout = new LayoutModel() {Height = 10000, Width = 10000};
                });
            }
        }

        public RelayCommand LoadAreaCommand
        {
            get
            {
                return new RelayCommand(() =>
                {
                    // new XmlSerializer(typeof (StationRelationModel));
                    //if(!File.Exists(Environment.SpecialFolder.ApplicationData + "\\LayoutTool\\MyFile.bin"))
                    //    return;
                    // var datfile = Environment.SpecialFolder.ApplicationData + "\\LayoutTool\\MyFile.bin";
                    //LoadProject();
#if DEBUG
                    var service = new CollaborationService.ServiceContractClient("BasicHttpsBinding_IServiceContractDebug");
#else
                    var service = new  CollaborationService.ServiceContractClient("BasicHttpsBinding_IServiceContract");
#endif
                    LoadProject(service.GetProjectArea("def", "def"));
                });
            }
        }

        public void LoadProject(ProjectArea project)
        {
            Model.LoadedStations.Clear();
            Model.CurrentWorkOrder.Worktasks.Clear();
            Model.DeployedGraficalObjects.Clear();
            if(project == null)
                return;
            StationRelationModel obj;
            using (var stream = new MemoryStream(project?.DetailedLayout ?? new byte[0]))
            {
                IFormatter formatter = new BinaryFormatter(new SurrogateSelector(), new StreamingContext());
                //var stream =  File.Open(datfile, FileMode.Open);
                try { obj = (StationRelationModel)formatter.Deserialize(stream); }
                catch
                {
                    Model.LoadedStations.Clear();
                    Model.CurrentWorkOrder.Worktasks.Clear();
                    Model.DeployedGraficalObjects.Clear();
                    return;
                }
                stream.Close();
            }
            



            //Layout = obj.Layout;


            foreach (var s in obj.LoadedStations)
            {
                Model.LoadedStations.Add(s);
                s.InitTransforms();
            }
            foreach (var s in obj.DeployedGraficalObjects)
            {
                Model.DeployedGraficalObjects.Add(s);
                s.InitTransforms();
            }

            foreach (var s in obj.CurrentWorkOrder.Worktasks)
                Model.CurrentWorkOrder.Worktasks.Add(s);
        }

        public RelayCommand SaveAreaCommand
        {
            get
            {
                return new RelayCommand(async () =>
                {
                    Directory.CreateDirectory(Environment.SpecialFolder.ApplicationData + "\\LayoutTool\\");
                    IFormatter formatter = new BinaryFormatter(new SurrogateSelector(), new StreamingContext());

                    MemoryStream ms = new MemoryStream();

                    //Stream stream = new FileStream(Environment.SpecialFolder.ApplicationData + "\\LayoutTool\\MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None);

                    formatter.Serialize(ms, Model);
                    ms.Position = 0;
                    var blub = ms.ToArray();
                    ms.Close();
                    ms.Dispose();

                    ms = new MemoryStream();

                    //Stream stream = new FileStream(Environment.SpecialFolder.ApplicationData + "\\LayoutTool\\MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None);

                    formatter.Serialize(ms, Model.Layout);
                    ms.Position = 0;
                    var blub2 = ms.ToArray();
                    ms.Close();
                    ms.Dispose();
#if DEBUG
                    var service = new  CollaborationService.ServiceContractClient("BasicHttpsBinding_IServiceContractDebug");
#else
                    var service = new  CollaborationService.ServiceContractClient("BasicHttpsBinding_IServiceContract");
#endif
                    
                    var layout =new Layout() {LayoutName = Layout.LayoutName, LayoutElement = Model.Layout.Layout.GetContractData(),PointCloudReference = Layout.Pointcloud, Width = Layout.Width, Height = Layout.Height};
                    
                    service.AddLayout(layout);
                    string pn = null;
                    foreach (var projectViewModel in ViewModelLocator.Layouts.ProjectsInLayout)
                    {
                        
                    
                    
                        service.AddProjectArea(Layout.LayoutName,
                            ViewModelLocator.Layouts.SelectedProject.Project.ProjectAreaName,
                            ViewModelLocator.Layouts.SelectedProject.Project.ProjectAreaDef, blub);
                        pn = ViewModelLocator.Layouts.SelectedProject.Project.ProjectAreaName;
                    }
                    
                    await ViewModelLocator.Layouts.UpdateProjects(Layout.LayoutName);
                    if(pn==null)
                        return;
                    ViewModelLocator.Layouts.SelectedProject =
                        ViewModelLocator.Layouts.ProjectsInLayout.FirstOrDefault(
                            (t) => t?.Project?.ProjectAreaName == pn);
                });
            }
        }


        public RelayCommand DrawObsticle
        {
            get { return new RelayCommand(() => { DrawNewSquare = DrawType.Obstacle; }); }
        }

        public RelayCommand DrawStorageSpace
        {
            get { return new RelayCommand(() => DrawNewSquare = DrawType.Storage); }
        }

        public RelayCommand DrawProjectArea
            => new RelayCommand(() =>
            {
                ViewModelLocator.Layouts.SelectedProject = null;
                DrawNewSquare = DrawType.Project;
            });



        public DrawType DrawNewSquare
        {
            get { return _drawNewSquare; }
            set
            {
                if (_drawNewSquare == value)
                    return;
                _drawNewSquare = value;
                
                RaisePropertyChanged();
            }
        }

        


        public RelayCommand<Point> StartDraw
            => new RelayCommand<Point>((point) => _startpoint = point);


        public RelayCommand<Point> EndDraw
        {
            get
            {
                return new RelayCommand<Point>(async (point) =>
                {
                    if (DrawNewSquare == DrawType.Obstacle)
                    {
                        var o = new GraficalObstacle(_startpoint, point);
                        Model.DeployedGraficalObjects.Add(o);
                        o.Position = new Point(_startpoint.X, _startpoint.Y);
                        o.NormalColor = Brushes.DarkOrange;
                        
                    }
                    else if (DrawNewSquare == DrawType.Storage)
                    {
                        var o = new GraficalStorageSpace(_startpoint, point);
                        SetStorageName(o, "New Storage");
                        Model.DeployedGraficalObjects.Add(o);
                        o.Position = new Point(_startpoint.X, _startpoint.Y);
                        o.NormalColor = Brushes.LightGreen;
                        
                    }
                    else if (DrawNewSquare == DrawType.Project)
                    {
                        int top = (int) Math.Min(_startpoint.Y, point.Y);
                        int bottom = (int)Math.Max(_startpoint.Y, point.Y);
                        int left = (int)Math.Min(_startpoint.X, point.X);
                        int right = (int)Math.Max(_startpoint.X, point.X);
                        var o = Rectangle.FromLTRB(left,top,right,bottom);

                        await ViewModelLocator.Layouts.AddNewProject(Layout.LayoutName, o);

                    }
                    DrawNewSquare = DrawType.Nothing;
                });
            }
        }

        public bool DragOffset
        {
            get { return _dragOffset; }
            set
            {
                if (_dragOffset == value)
                    return;
                _dragOffset = value;
                RaisePropertyChanged();
            }
        }


        public ListBoxItem SelectedNewTask
        {
            get { return _selectedNewTask; }
            set
            {
                _selectedNewTask = value;
                GeneralTask.WorkTaskType outp;
                if (!Enum.TryParse(value.Name, true, out outp))
                    outp = GeneralTask.WorkTaskType.General;

                NewTask = GeneralTask.CreateTask(outp, NewTask?.TaskFromStation as Station);
                SetTaskName(NewTask.TaskType.ToString());
                RaisePropertyChanged();
            }
        }

        public RelayCommand AddTask
        {
            get
            {
                return new RelayCommand(() =>
                {
                    if (NewTask?.TaskType == GeneralTask.WorkTaskType.Walk)
                    {
                        CurrentWorkOrder.Worktasks.Add(NewTask);
                        NewTask = GeneralTask.CreateTask(NewTask.TaskType, NewTask.TaskToStation as Station);
                    }
                    else if (NewTask?.TaskType == GeneralTask.WorkTaskType.Work)
                    {
                        CurrentWorkOrder.Worktasks.Add(NewTask);
                        NewTask = GeneralTask.CreateTask(NewTask.TaskType, NewTask.TaskToStation as Station);
                    }
                    else
                    {
                        CurrentWorkOrder.Worktasks.Add(NewTask);
                        NewTask = new GeneralTask(NewTask?.TaskType ?? GeneralTask.WorkTaskType.General,
                            NewTask?.TaskFromStation as Station)
                        {
                            TaskTime = NewTask?.TaskTime ?? TimeSpan.Zero,
                            TaskToStation = NewTask?.TaskToStation,
                        };
                    }

                    SetTaskName("New" + NewTask.TaskType + "Task");
                    ShowSpagetti = true;
                });
            }
        }

        public RelayCommand<GeneralTask> DeleteTask
        {
            get { return new RelayCommand<GeneralTask>(t => CurrentWorkOrder.Worktasks.Remove(t)); }
        }

        public GeneralTask NewTask
        {
            get { return _newTask; }
            set
            {
                if (value == _newTask)
                    return;
                _newTask = value;
                RaisePropertyChanged();
            }
        }

        public RelayCommand SendToImma
        {
            get
            {
                return new RelayCommand(() =>
                {
                    var f = new ImmaExportModelLoadSaveService();
                    f.SaveToFile(Model.DeployedGraficalObjects.ToArray(), CurrentWorkOrder, Layout);
                });
            }
        }

        public RelayCommand<GraficalObject> SelectGraficalObject
        {
            get { return new RelayCommand<GraficalObject>((station) => { SelectedGraficalObject = station; }); }
        }

        public IEnumerable<StationRelation> RelationsWithSoundSensitivness
        {
            get
            {
                List<StationRelation> list = new List<StationRelation>();
                foreach (Station t in DeployedStations)
                {
                    if (t.SoundSensitiv)
                        foreach (var loud in DeployedStations.Where(p => ((int) p.Loudness) > 0))
                            list.Add(new StationRelation(t, loud, (int) loud.Loudness));
                }
                return list;
            }
        }

        public LayoutResults Results
        {
            get { return _results; }
            set
            {
                _results = value;
                RaisePropertyChanged();
            }
        }

        public RelayCommand LoadImma { get; } = new RelayCommand(async () =>
        {
            var m = new ImmaExportModelLoadSaveService();
            var sim = await m.LoadFile();
            LayoutResults res = new LayoutResults();

            res.OpTime = sim?.SimultionResult?.Paths?.FirstOrDefault()?.TimeSpanType ?? TimeSpan.Zero;
            res.EgroScore = sim?.SimultionResult?.Paths?.FirstOrDefault()?.ErgonomicScore ?? 0;
            ViewModelLocator.Main.Results = res;
        });

        public double Zoom
        {
            get { return _zoom; }
            set
            {
                if(value <0.00001)
                    return;

                var zenterx = Offset.X  + Layout.Width / 2 ;
                var zentery = Offset.Y  + Layout.Height / 2;
               
                _zoom = value;
                
                RaisePropertyChanged();
                Offset.X = (zenterx - Layout.Width /2);
                Offset.Y = (zentery - Layout.Height/2);
            }
        }
        public RelayCommand<MouseWheelEventArgs> ZoomMouseWheel
          => new RelayCommand<MouseWheelEventArgs>((e) => { 
              var n = Zoom + Zoom* 0.005*e.Delta;
              Zoom = n;
          });

        public Model.Point Offset => _offset;

        public RelayCommand<bool> DragOffsetChanged
            => new RelayCommand<bool>((e) =>
            {
                DragOffset = e;
            });

        private void Offset_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            RaisePropertyChanged(nameof(Offset));
        }

        private void DeployedGraficalObjects_CollectionChanged(object sender,
            NotifyCollectionChangedEventArgs e)
        {
            RaisePropertyChanged(nameof(DeployedStations));
            RaisePropertyChanged(nameof(DeployedStorages));
            RaisePropertyChanged(nameof(Obstacles));
            RaisePropertyChanged(nameof(RelationsWithSoundSensitivness));
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (Station item in e.NewItems.OfType<Station>())
                    {
                        item.PropertyChanged += StationPropertyChanged;
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:

                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (Station item in e.OldItems.OfType<Station>())
                    {
                        item.PropertyChanged -= StationPropertyChanged;
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    foreach (Station item in e.OldItems.OfType<Station>())
                    {
                        item.PropertyChanged -= StationPropertyChanged;
                    }
                    foreach (Station item in e.NewItems.OfType<Station>())
                    {
                        item.PropertyChanged += StationPropertyChanged;
                    }
                    break;
            }
        }

        private void StationPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Loudness" || e.PropertyName == "SoundColor" || e.PropertyName == "SoundSensitiv")
                RaisePropertyChanged(nameof(RelationsWithSoundSensitivness));
        }

        private void AddStationToLayout(Station newStation)
        {
            if (!Box && File.Exists(ImportFile))
            {
                var figure = ImportPathFigures(ImportFile);
                var figures = new PathFigure[] {figure};
                ViewModelLocator.Dispatcher.Invoke(() =>
                {
                    var shape = new PathGeometry(figures);
                    newStation.SetCustomControl(new ContentControl() {Content = shape});
                    if (shape.Bounds.Y < 0)
                        newStation.Position.Y = -shape.Bounds.Y;
                    if (shape.Bounds.X < 0)
                        newStation.Position.X = -shape.Bounds.X;
                });
            }
            newStation.PropertyChanged += NewStation_PropertyChanged;
            SelectedGraficalObject = newStation;
            Model.DeployedGraficalObjects.Add(newStation);
        }

        public void SetTaskName(string value)
        {
            int i = 0;
            while (CurrentWorkOrder.Worktasks.Any(t => t.TaskName == (i > 0 ? value + i : value)))
                i++;
            if (i > 0)
                value += i;
            NewTask.TaskName = value;
        }


        private void SetStorageName(GraficalStorageSpace graficalStorageSpace, string value)
        {
            int i = 0;
            while (DeployedStorages.Any(t => t.Name == (i > 0 ? value + i : value)))
                i++;
            if (i > 0)
                value += i;
            graficalStorageSpace.Name = value;
        }


        private void NewStation_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (!_running && e.PropertyName == "Position" || e.PropertyName == "NegativeInfluence")
            {
                _running = true;
                RaisePropertyChanged("Width");
                RaisePropertyChanged("Height");
                //foreach (var station in DeployedGraficalObjects.Where(t => t != sender))
                {
                    //station.RefreshPos();
                }

                _running = false;
            }
        }

        private async Task Initialize()
        {
            try
            {
                var item = await _service.LoadStationRelationService(null);
                Model = item;
                foreach (var station in Stations)
                {
                    station.PropertyChanged += NewStation_PropertyChanged;
                }
            }
            catch (Exception)
            {
                // Report error here
            }
        }

        private IList<Point> GetPolygonOfObject(FrameworkElement element, FrameworkElement grid)
        {
            Rect rectangleBounds = new Rect(0, 0, element.RenderSize.Width, element.RenderSize.Height);
            //GeneralTransform transform = element.TransformToVisual(rectangleBounds);
            var transform = element.TransformToVisual(grid);

            rectangleBounds = transform.TransformBounds(rectangleBounds);
            var points = new Point[4];
            points[0] = transform.Transform(new Point(0, 0));
            points[2] = transform.Transform(new Point(element.ActualWidth, element.ActualHeight));
            points[1] = transform.Transform(new Point(element.ActualWidth, 0));
            points[3] = transform.Transform(new Point(0, element.ActualHeight));
            //double minX = Math.Min(Math.Min(lt.X, rt.X), Math.Min(lb.X, rb.X));
            //double minY = Math.Min(Math.Min(lt.Y, rt.Y), Math.Min(lb.Y, rb.Y));
            //double maxX = Math.Max(Math.Max(lt.X, rt.X), Math.Max(lb.X, rb.X));
            //double maxY = Math.Max(Math.Max(lt.Y, rt.Y), Math.Max(lb.Y, rb.Y));


            return points;


            // return new Rect(minX, minY, (maxX - minX), (maxY - minY));
        }

        private DependencyObject _overlapGridder;
        private async Task<bool> Overlapps(FrameworkElement element, FrameworkElement element2)
        {
            return await element.Dispatcher.InvokeAsync<bool>(() =>
            {
                if (element == null || element2 == null)
                    return false;
              
                if(_overlapGridder == null)
                    _overlapGridder = element.FindCommonVisualAncestor(element2);
                
                IList<Point> rect = null, rect2 = null;
                try
                {
                    rect = GetPolygonOfObject(element, (FrameworkElement) _overlapGridder);
                    rect2 = GetPolygonOfObject(element2, (FrameworkElement) _overlapGridder);
                }
                catch(Exception e) { }
                if (rect == null || rect2 == null)
                {
                   return false;
                }
                
                return Collision.PolygonCollision(rect, rect2);

            });
        }

        private bool CrossingBorder(FrameworkElement element, Rectangle rect2)
        {
            if (_overlapGridder == null)
                return true;
            if (rect2 == null)
                return false;
            IList<Point> rect=null;
            element.Dispatcher.Invoke(() =>
            {
                rect = GetPolygonOfObject(element, (FrameworkElement)_overlapGridder);
            });
            
            foreach (var p in rect)
                if (p.X < rect2.X || p.X > rect2.X + rect2.Width || p.Y < rect2.Y || p.Y > rect2.Y + rect2.Height)
                    return true;
            return false;
            
        }

        public async Task<List<GraficalObject>> OverlappingStations(GraficalObject s)
        {
            var ds = new List<GraficalObject>();
            foreach (var source in DeployedGraficalObjects.Except(new []{s}))
            {
                if (await Overlapps((s.Control),source.Control))
                    ds.Add(source);
            }
            return ds;
        }

#region ImportPath

        private PathFigure ImportPathFigures(string importFile)
        {
            PathFigure figs = null;
            ViewModelLocator.Dispatcher.Invoke(() => figs = new PathFigure());
            using (StreamReader sr = new StreamReader(importFile))
            {
                ViewModelLocator.Dispatcher.Invoke(() => figs.StartPoint = getCords(sr.ReadLine()));
                Point current;
                while (!sr.EndOfStream)
                {
                    current = getCords(sr.ReadLine());
                    ViewModelLocator.Dispatcher.Invoke(() => figs.Segments.Add(new LineSegment(current, true)));
                }
            }
            return figs;
        }


        private Point getCords(string readLine)
        {
            try
            {
                var s = readLine.Split(';');
                var posx = double.Parse(s[0], CultureInfo.InvariantCulture);
                var posy = double.Parse(s[1], CultureInfo.InvariantCulture);
                return new Point(posx, posy);
            }
            catch
            {
                return new Point();
            }
        }

#endregion
    }

    public enum DrawType
    {
        Nothing,Storage,Obstacle,Project 
    }
}